package com.cq.hd.order.service.impl;

import cn.hutool.core.bean.BeanUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.cq.hd.common.constant.Constant;
import com.cq.hd.common.constant.MsgConstant;
import com.cq.hd.common.entity.mq.NotifyMsgSendDto;
import com.cq.hd.common.enums.biz.*;
import com.cq.hd.common.response.Throw;
import com.cq.hd.common.utils.GenerateUtil;
import com.cq.hd.common.utils.LocalDateUtil;
import com.cq.hd.common.utils.NoUtils;
import com.cq.hd.member.api.*;
import com.cq.hd.member.api.dto.*;
import com.cq.hd.member.api.vo.*;
import com.cq.hd.order.api.vo.OrderItemPointsVo;
import com.cq.hd.order.api.vo.OrderPointsVo;
import com.cq.hd.order.config.HDFinancialAccountProperties;
import com.cq.hd.order.config.LockUtils;
import com.cq.hd.order.mapper.TbOrderItemMapper;
import com.cq.hd.order.mapper.TbOrderMapper;
import com.cq.hd.order.mq.provider.NotifyMsgProvider;
import com.cq.hd.order.param.SettleParam;
import com.cq.hd.order.param.SubOrderStateUpdateParam;
import com.cq.hd.order.po.TbOrderItemPo;
import com.cq.hd.order.po.TbOrderPo;
import com.cq.hd.order.po.TbOrderSettleBillItemPo;
import com.cq.hd.order.po.TbOrderSettleBillPo;
import com.cq.hd.order.service.HfRemoteCallerService;
import com.cq.hd.order.service.OrderSettleService;
import com.cq.hd.order.service.TbOrderSettleBillItemService;
import com.cq.hd.order.service.TbOrderSettleBillService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.interceptor.DefaultTransactionAttribute;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.text.MessageFormat;
import java.time.LocalDateTime;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * 订单结算service
 *
 * @author much
 * @since 2024/05/03 16:20
 */
@Slf4j
@Service
public class OrderSettleServiceImpl implements OrderSettleService {

    @Resource
    private TbOrderMapper orderMapper;
    @Resource
    private TbOrderItemMapper orderItemMapper;
    @Resource
    private TbOrderSettleBillService orderSettleBillService;
    @Resource
    private TbOrderSettleBillItemService orderSettleBillItemService;
    @Resource
    private TbMerchantApi merchantApi;
    @Resource
    private TbBusinessApi businessApi;
    @Resource
    private TbBusinessMerchantApi businessMerchantApi;
    @Resource
    private TbAgentEnterApplyApi agentEnterApplyApi;
    @Resource
    private TbAppUserApi appUserApi;
    @Resource
    private HDFinancialAccountProperties hdFinancialAccountProperties;
    @Resource
    private PlatformTransactionManager transactionManager;
    @Resource
    private HfRemoteCallerService hfRemoteCallerService;
    @Resource
    private NotifyMsgProvider notifyMsgProvider;
    @Resource
    private TbBusinessPointsMallApi tbBusinessPointsMallApi;

    /**
     * 结算任务
     */
    @Override
    public void settleTask() {
        // 查询待结算的子订单（核销后T+1）
        List<TbOrderItemPo> waitSettleOrderItems = orderItemMapper.selectUnSettleOrderItems(LocalDateTime.now().minusDays(1));
        SettleParam settleParam = initializeSettleParam(waitSettleOrderItems);
        for (TbOrderItemPo orderItem : waitSettleOrderItems) {
            settle(settleParam, orderItem);
        }
    }

    /**
     * 子订单结算积分[停用]
     */
    @Override
    public void subOrderSettlePointsTask() {
//        List<OrderPointsVo> orderPointsVos = orderItemMapper.selectUnSettlePointsOrders();
//        for (OrderPointsVo orderPointsVo : orderPointsVos) {
//            settlePoints(orderPointsVo);
//        }
//        List<OrderItemPointsVo> pointsVos = orderItemMapper.selectUnSettlePointsOrderItems();
//        for (OrderItemPointsVo pointsVo : pointsVos) {
//            settlePoints(pointsVo);
//        }
    }

    /**
     * 下单用户积分结算
     */
    @Override
    public void settleUserPointsTask() {
        List<OrderPointsVo> orderPointsVos = orderMapper.selectUserUnSettlePointsOrders();
        for (OrderPointsVo orderPointsVo : orderPointsVos) {
            settlePoints(orderPointsVo, "user_points_settle_state");
        }
    }

    /**
     * 经纪人积分结算
     */
    @Override
    public void settleAgentPointsTask() {
        List<OrderPointsVo> orderPointsVos = orderMapper.selectAgentUnSettlePointsOrders();
        for (OrderPointsVo orderPointsVo : orderPointsVos) {
            settlePoints(orderPointsVo, "agent_points_settle_state");
        }
    }

    private void settlePoints(OrderPointsVo pointsVo, String field) {
        String lockKey = MessageFormat.format(Constant.LOCK_FORMAT, "settleOrderPoints:" + pointsVo.getOrderNo());
        try {
            // 加锁避免被重复调用
            LockUtils.blockLock(lockKey, Constant.AUTO_UNLOCK_SECOND);

            TransactionStatus transaction = transactionManager.getTransaction(new DefaultTransactionAttribute());
            try {
                // 查询是否存在已结算积分的子单，如果有则先扣除（旧逻辑是根据子单的积分进行结算，现在改成取父单积分，需要避免重复结算）
                List<TbOrderItemPo> settledPointItems = listSettledPointOrderItems(pointsVo.getId());

                // 一个子单核销，即给全部的积分奖励 20240907
                int updateResult = orderMapper.updatePointsSettleState(pointsVo.getId(), field);
                if (updateResult == 0) {
                    log.error("更新订单积分结算状态失败，orderNo = {}", pointsVo.getOrderNo());
                    Throw.isBusinessException("更新订单积分结算状态失败");
                }

                // 调用积分商城接口，增加用户积分
                OrderItemPointsSettleDto dto = new OrderItemPointsSettleDto();
                BeanUtil.copyProperties(pointsVo, dto);
                // 重新计算积分，需要扣掉已发放的积分
                if (CollectionUtils.isNotEmpty(settledPointItems)) {
                    for (TbOrderItemPo orderItem : settledPointItems) {
                        if (orderItem.getUserPointsAmt() != null && dto.getUserPointsAmt() != null) {
                            dto.setUserPointsAmt(dto.getUserPointsAmt().subtract(orderItem.getUserPointsAmt()));
                        }
                        if (orderItem.getAgentPointsAmt() != null && dto.getAgentPointsAmt() != null) {
                            dto.setAgentPointsAmt(dto.getAgentPointsAmt().subtract(orderItem.getAgentPointsAmt()));
                        }
                        if (orderItem.getSubAgentPointsAmt() != null && dto.getSubAgentPointsAmt() != null) {
                            dto.setSubAgentPointsAmt(dto.getSubAgentPointsAmt().subtract(orderItem.getSubAgentPointsAmt()));
                        }
                    }
                }

                boolean update = tbBusinessPointsMallApi.subOrderSettlePoints(dto).unpack();
                if (!update) {
                    log.error("子订单结算，积分发放失败，orderNo = {}", pointsVo.getOrderNo());
                    Throw.isBusinessException("积分发放失败");
                }

                transactionManager.commit(transaction);
            } catch (Exception e) {
                //回滚事务
                transactionManager.rollback(transaction);
                log.error("<子订单结算积分>事务异常回滚", e);
                Throw.isBusinessException(e.getMessage());
            }
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        } finally {
            LockUtils.unlock(lockKey);
        }
    }

    // 查询已经结算了积分的子单
    private List<TbOrderItemPo> listSettledPointOrderItems(Long orderId) {
        LambdaQueryWrapper<TbOrderItemPo> qw = Wrappers.<TbOrderItemPo>lambdaQuery()
                .eq(TbOrderItemPo::getOrderId, orderId)
                .eq(TbOrderItemPo::getPointsSettleState, 2);
        return orderItemMapper.selectList(qw);
    }

    private void settlePoints(OrderItemPointsVo pointsVo) {
        String lockKey = MessageFormat.format(Constant.LOCK_FORMAT, "settleOrderItemPoints:" + pointsVo.getSubOrderNo());
        try {
            // 加锁避免被重复调用
            LockUtils.blockLock(lockKey, Constant.AUTO_UNLOCK_SECOND);

            TransactionStatus transaction = transactionManager.getTransaction(new DefaultTransactionAttribute());
            try {
                // 再次查询最新的子单数据

                // 查询是否存在已结算积分的子单，如果有则先扣除（旧逻辑是根据子单的积分进行结算，现在改成取父单积分，需要避免重复结算）

                // 一个子单核销，即给全部的积分奖励 20240907

                int updateResult = orderItemMapper.updatePointsSettleState(pointsVo.getId());
                if (updateResult == 0) {
                    log.error("更新子单积分结算状态失败，subOrderNo = {}", pointsVo.getSubOrderNo());
                    Throw.isBusinessException("更新子单积分结算状态失败");
                }

                // 调用积分商城接口，增加用户积分
                OrderItemPointsSettleDto dto = new OrderItemPointsSettleDto();
                BeanUtil.copyProperties(pointsVo, dto);
                boolean update = tbBusinessPointsMallApi.subOrderSettlePoints(dto).unpack();
                if (!update) {
                    log.error("子订单结算，积分发放失败，subOrderNo = {}", pointsVo.getSubOrderNo());
                    Throw.isBusinessException("积分发放失败");
                }

                transactionManager.commit(transaction);
            } catch (Exception e) {
                //回滚事务
                transactionManager.rollback(transaction);
                log.error("<子订单结算积分>事务异常回滚", e);
                Throw.isBusinessException(e.getMessage());
            }
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        } finally {
            LockUtils.unlock(lockKey);
        }
    }

    /**
     * 对子订单进行结算
     *
     * @param orderItem 子订单信息
     */
    @Override
    public void settle(SettleParam settleParam, TbOrderItemPo orderItem) {
        // lock by sub_order_no
        String lockKey = getLockKey4Settle(orderItem.getSubOrderNo());
        try {
            // 加锁避免被重复调用
            LockUtils.blockLock(lockKey, Constant.AUTO_UNLOCK_SECOND);

            // 1.订单状态校验
            validateOrderState(orderItem);

            // 2.计算商品本金、补贴和佣金
            List<TbOrderSettleBillPo> orderSettleBills = computeBills(settleParam, orderItem);

            // 3.商户、用户相关金额及流水
            OrderSettleUpdateDto orderSettleUpdateDto = getOrderSettleUpdateDto(settleParam, orderItem, orderSettleBills);

            // 4.开启事务，保存结算单和增加用户余额变更
            performOrderItemSettleTransaction(orderItem, orderSettleBills, orderSettleUpdateDto);

            // 5.异步调用汇付转账
            hfRemoteCallerService.asyncCallByOrderSettle(orderSettleBills);

            // 6.给商户发送结算系统通知
            sendOrderSettleNotification(orderItem, orderSettleBills);

        } catch (Exception e) {
            log.error(e.getMessage(), e);
        } finally {
            LockUtils.unlock(lockKey);
        }
    }

    /**
     * 发送订单结算通知
     *
     * @param orderItem 子单信息
     * @param orderSettleBills 结算单
     */
    private void sendOrderSettleNotification(TbOrderItemPo orderItem, List<TbOrderSettleBillPo> orderSettleBills) {
        if (CollectionUtils.isEmpty(orderSettleBills)) {
            return;
        }
        BigDecimal merchantAmt = orderSettleBills.stream()
                .filter(e -> OrderSettleBillTradeTypeEnum.MERCHANT_AMT.getValue().equals(e.getTradeType()))
                .map(TbOrderSettleBillPo::getTradeAmt).reduce(BigDecimal.ZERO, BigDecimal::add);

        BigDecimal subsidyAmt = orderSettleBills.stream()
                .filter(e -> OrderSettleBillTradeTypeEnum.SUBSIDY_AMT.getValue().equals(e.getTradeType()))
                .map(TbOrderSettleBillPo::getTradeAmt).reduce(BigDecimal.ZERO, BigDecimal::add);

        NotifyMsgSendDto notifyMsgSendDto = new NotifyMsgSendDto();
        notifyMsgSendDto.setUserId(orderItem.getMerchantId());
        notifyMsgSendDto.setRoleType(RoleTypeEnum.MERCHANT.getValue());
        notifyMsgSendDto.setMsgType(NotifyMsgTypeEnum.MERCHANT_ORDER_SETTLE.getValue());
        notifyMsgSendDto.setContent(String.format(MsgConstant.MERCHANT_ORDER_SETTLE, orderItem.getSubOrderNo(),
                merchantAmt,
                subsidyAmt));
        notifyMsgSendDto.setMsgState(NotifyMsgStateEnum.NO_READ.getValue());
        notifyMsgProvider.sendMsg(notifyMsgSendDto);
    }

    private void performOrderItemSettleTransaction(
            TbOrderItemPo orderItem, List<TbOrderSettleBillPo> orderBills, OrderSettleUpdateDto orderSettleUpdateDto) {
        TransactionStatus transaction = transactionManager.getTransaction(new DefaultTransactionAttribute());
        try {
            // 子单更新为已结算
            SubOrderStateUpdateParam subOrderStateUpdateParam = new SubOrderStateUpdateParam();
            subOrderStateUpdateParam.setId(orderItem.getId());
            subOrderStateUpdateParam.setOrgSubOrderState(orderItem.getSubOrderState());
            subOrderStateUpdateParam.setSubOrderState(SubOrderStateEnum.SETTLED.getValue());
            subOrderStateUpdateParam.setUpdateTime(LocalDateTime.now());
            subOrderStateUpdateParam.setSettleTime(LocalDateTime.now());
            int updateResult = orderItemMapper.updateSubState(subOrderStateUpdateParam);
            if (updateResult == 0) {
                log.error("更新子单状态失败，subOrderNo = {}", orderItem.getSubOrderNo());
                Throw.isBusinessException("更新子单状态失败");
            }
            // 更新订单经纪人积分结算标识
            orderMapper.setAgentPointsSettleState(orderItem.getOrderId());
            // 更新订单已结算佣金
            BigDecimal currentSettledCommissionAmt = orderBills.stream()
                    .filter(e -> e.getTradeType().equals(OrderSettleBillTradeTypeEnum.COMMISSION_AMT.getValue()))
                    .map(TbOrderSettleBillPo::getTradeAmt)
                    .reduce(BigDecimal.ZERO, BigDecimal::add);
            orderMapper.updateSettledCommissionAmt2(orderItem.getOrderId(), currentSettledCommissionAmt);

            // FIXME 更新子订单佣金结算状态（一个子单结算，即结算全部佣金）
            orderItemMapper.updateCommissionSettleStateByOrderId(orderItem.getOrderId());

//            orderMapper.updateSettledCommissionAmt(orderItem.getOrderId());
            // 保存结算账单数据（包含item）
            if (CollectionUtils.isNotEmpty(orderBills)) {
                boolean res = orderSettleBillService.saveBatch(orderBills);
                if (!res) {
                    log.error("订单结算，保存结算单失败，subOrderNo = {}", orderItem.getSubOrderNo());
                    Throw.isBusinessException("订单结算，保存结算单失败");
                }
                // 插入OrderSettleBillItem for 月度结算有使用（历史遗留问题）
                List<TbOrderSettleBillItemPo> orderSettleBillItems = convertToBillItems(orderItem.getSubOrderNo(), orderBills);
                res = orderSettleBillItemService.saveBatch(orderSettleBillItems);
                if (!res) {
                    log.error("订单结算，保存结算单Item失败，subOrderNo = {}", orderItem.getSubOrderNo());
                    Throw.isBusinessException("订单结算，保存结算单Item失败");
                }
                orderSettleUpdateDto.setAutoWithdrawalRecords(generateAutoWithdrawalRecords(orderBills));
            }

            // 自动提现记录
            // 更新商户本金，商户补贴和经纪人佣金，添加商户资金流水和经纪人佣金流水，统计商家与经纪人的累计结算分销佣金
            Boolean updateOrderSettleAmtState = appUserApi.updateOrderSettleAmt(orderSettleUpdateDto).unpack();
            if (updateOrderSettleAmtState == null || !updateOrderSettleAmtState) {
                log.error("更新订单结算金额失败，subOrderNo：{}", orderItem.getSubOrderNo());
                Throw.isBusinessException("更新订单结算金额失败");
            }

            transactionManager.commit(transaction);
        } catch (Exception e) {
            //回滚事务
            transactionManager.rollback(transaction);
            log.error("<订单结算>事务异常回滚", e);
            Throw.isBusinessException(e.getMessage());
        }
    }

    private String getLockKey4Settle(String subOrderNo) {
        return MessageFormat.format(Constant.LOCK_FORMAT, "settleOrderItem:" + subOrderNo);
    }

    private void validateOrderState(TbOrderItemPo orderItemPo) {
        // 查询最新的记录用于状态的判断
        TbOrderItemPo tbOrderItemPo = orderItemMapper.selectById(orderItemPo.getId());
        if (SubOrderStateEnum.SETTLED.getValue().equals(tbOrderItemPo.getSubOrderState())) {
            Throw.isBusinessException("子订单[" + orderItemPo.getSubOrderNo() + "] 已是结算状态");
        }
        // 判断一下主订单是否已结算（旧的结算状态是在主订单上，避免被重复结算）
        TbOrderPo tbOrderPo = orderMapper.selectById(orderItemPo.getOrderId());
        if (OrderStateEnum.FINISH.getValue().equals(tbOrderPo.getOrderState())) {
            Throw.isBusinessException("主订单[" + tbOrderPo.getOrderNo() + "] 已是结算状态");
        }
    }

    private List<TbOrderSettleBillItemPo> convertToBillItems(String subOrderNo, List<TbOrderSettleBillPo> orderBills) {
        return orderBills.stream()
                .map(orderBill -> {
                    TbOrderSettleBillItemPo settleBillItem = BeanUtil.copyProperties(orderBill, TbOrderSettleBillItemPo.class);
                    settleBillItem.setSubOrderNo(subOrderNo);
                    settleBillItem.setBillId(orderBill.getId());
                    return settleBillItem;
                })
                .collect(Collectors.toList());
    }

    /**
     * 计算商品本金、补贴和佣金
     *
     * @param orderItem 子订单
     */
    private List<TbOrderSettleBillPo> computeBills(SettleParam settleParam, TbOrderItemPo orderItem) {
        List<TbOrderSettleBillPo> orderSettleBills = new ArrayList<>();
        // 商品本金
        TbOrderSettleBillPo merchantAmtSettleBill = createMerchantAmtSettleBill(settleParam, orderItem);
        if (merchantAmtSettleBill != null) {
            orderSettleBills.add(merchantAmtSettleBill);
        }
        // 商品补贴
        TbOrderSettleBillPo subsidyAmtSettleBill = createSubsidyAmtSettleBill(settleParam, orderItem);
        if (subsidyAmtSettleBill != null) {
            orderSettleBills.add(subsidyAmtSettleBill);
        }

        // 判断是否已经计算过佣金
        TbOrderItemPo newOrderItem = orderItemMapper.selectById(orderItem);
        if (newOrderItem.getCommissionSettleState() == null || newOrderItem.getCommissionSettleState() != 2) {
            // 一级经纪人佣金
            // TODO 如果同一个父单下，存在已经结算的子单，则需要扣除对应佣金（旧逻辑是通过子单上的佣金结算给用户，现在改成从父单取，需要避免已结算过的多给，执行一段时间可以移除该判断）
            TbOrderPo order = orderMapper.selectById(orderItem.getOrderId());

            List<TbOrderItemPo> settledCommissionItems = listSettledCommissionOrderItems(orderItem.getOrderId());
            // 扣掉已结算的佣金
            for (TbOrderItemPo settledCommissionItem : settledCommissionItems) {
                if (settledCommissionItem.getCommissionAmt() != null) {
                    order.setActualCommissionAmt(order.getActualCommissionAmt().subtract(settledCommissionItem.getCommissionAmt()));
                }
                if (settledCommissionItem.getSubAgentCommissionAmt() != null) {
                    order.setSubAgentCommissionAmt(order.getSubAgentCommissionAmt().subtract(settledCommissionItem.getSubAgentCommissionAmt()));
                }
            }

            TbOrderSettleBillPo agentSettleBill = createAgentCommissionSettleBill(order, orderItem);
            if (agentSettleBill != null) {
                orderSettleBills.add(agentSettleBill);
            }
            // 二级经纪人佣金
            TbOrderSettleBillPo agentSettleBill2 = createSubAgentCommissionSettleBill(order, orderItem);
            if (agentSettleBill2 != null) {
                orderSettleBills.add(agentSettleBill2);
            }
        }


        return orderSettleBills;
    }

    private List<TbOrderItemPo> listSettledCommissionOrderItems(Long orderId) {
        LambdaQueryWrapper<TbOrderItemPo> qw = Wrappers.<TbOrderItemPo>lambdaQuery()
                .eq(TbOrderItemPo::getOrderId, orderId)
                .eq(TbOrderItemPo::getCommissionSettleState, 2);
        return orderItemMapper.selectList(qw);
    }

    private OrderSettleUpdateDto getOrderSettleUpdateDto(SettleParam settleParam, TbOrderItemPo orderItem, List<TbOrderSettleBillPo> orderSettleBills) {
        OrderSettleUpdateDto orderSettleUpdateDto = new OrderSettleUpdateDto();
        orderSettleUpdateDto.setOrderId(orderItem.getId());
        orderSettleUpdateDto.setAgentId(orderItem.getAgentId()); // 一级经纪人
        orderSettleUpdateDto.setAgent2Id(orderItem.getAgent2Id()); // 二级经纪人
        // 商户资金流水
        List<MerchantAmtBillSaveDto> merchantAmtBillSaveList = convertSettleBillsToMerchantAmtBills(settleParam, orderItem, orderSettleBills);
        orderSettleUpdateDto.setMerchantAmtBillSaves(merchantAmtBillSaveList);
        // 商家增加的余额
        orderSettleUpdateDto.setMerchantAmtAdds(getMerchantAmtAdd(merchantAmtBillSaveList));
        Long agent1Id = orderItem.getAgentId();
        Long agent2Id = orderItem.getAgent2Id();
        TbOrderPo order = orderMapper.selectById(orderItem.getOrderId());
        // 一级经纪人佣金
        if (agent1Id != null && agent1Id > 0) {
            setCommissionInfo(settleParam, orderSettleUpdateDto, order, orderItem, orderSettleBills, agent1Id);
        }
        // 二级经纪人佣金
        if (agent2Id != null && agent2Id > 0) {
            setCommissionInfo(settleParam, orderSettleUpdateDto, order, orderItem, orderSettleBills, agent2Id);
        }

        return orderSettleUpdateDto;
    }

    /**
     * 普通商户的本金，需要自动提现
     */
    private List<AutoWithdrawalRecordDto> generateAutoWithdrawalRecords(List<TbOrderSettleBillPo> orderSettleBills) {
        if (org.springframework.util.CollectionUtils.isEmpty(orderSettleBills)) {
            return Collections.emptyList();
        }
        return orderSettleBills.stream()
                // 商户本金 & 商户资金账户=自建账户
                .filter(e -> OrderSettleBillTradeTypeEnum.MERCHANT_AMT.getValue().equals(e.getTradeType())
                        && e.getMerchantFinancialAccountType() != null
                        && e.getMerchantFinancialAccountType() == FinancialAccountTypeEnum.SELF.getValue())
                .map(e -> {
                    AutoWithdrawalRecordDto autoWithdrawalRecordDto = new AutoWithdrawalRecordDto();
                    autoWithdrawalRecordDto.setBillId(e.getId());
                    autoWithdrawalRecordDto.setOrderId(e.getOrderId());
                    autoWithdrawalRecordDto.setBusinessId(e.getBusinessId());
                    autoWithdrawalRecordDto.setMerchantId(e.getMerchantId());
                    autoWithdrawalRecordDto.setWithdrawalAmt(e.getTradeAmt());
                    autoWithdrawalRecordDto.setState(AutoWithdrawalStateEnum.WAIT.getValue());
                    return autoWithdrawalRecordDto;
                })
                .collect(Collectors.toList());
    }

    /**
     * 设置佣金相关数据 一级经纪人
     */
    private void setCommissionInfo(SettleParam settleParam,
                                   OrderSettleUpdateDto orderSettleUpdateDto,
                                   TbOrderPo order,
                                   TbOrderItemPo orderItem,
                                   List<TbOrderSettleBillPo> settleBills,
                                   Long agentId) {
        List<TbOrderSettleBillPo> commissionSettleBills = settleBills.stream()
                .filter(e -> OrderSettleBillTradeTypeEnum.COMMISSION_AMT.getValue().equals(e.getTradeType()))
                .filter(e -> Objects.equals(e.getAgentId(), agentId))
                .collect(Collectors.toList());
        if (CollectionUtils.isEmpty(commissionSettleBills)) {
            return;
        }
        // 佣金
        BigDecimal commissionAmt = commissionSettleBills.stream()
                .map(TbOrderSettleBillPo::getTradeAmt).reduce(BigDecimal.ZERO, BigDecimal::add);
        // 佣金流水
        AppUserVo agent = settleParam.getAgent(agentId);
        CommissionBillSaveDto commissionBillSaveDto = new CommissionBillSaveDto();
        commissionBillSaveDto.setBillNo(NoUtils.getMemberAmtBillNo());
        commissionBillSaveDto.setUserId(agentId);
        commissionBillSaveDto.setBillType(BillTypeEnum.INCOME.getValue());
        commissionBillSaveDto.setTradeType(CommissionBillTradeTypeEnum.ORDER_COMMISSION.getValue());
        commissionBillSaveDto.setRelationTradeNo(order.getOrderNo());
        BigDecimal beforeCommissionAmt = agent.getCommissionAmt();
        BigDecimal afterCommissionAmt = beforeCommissionAmt.add(commissionAmt);
        agent.setCommissionAmt(afterCommissionAmt);
        commissionBillSaveDto.setBeforeAmt(beforeCommissionAmt);
        commissionBillSaveDto.setTradeAmt(commissionAmt);
        commissionBillSaveDto.setAfterAmt(afterCommissionAmt);
        LocalDateTime now = LocalDateTime.now();
        commissionBillSaveDto.setTradeTime(now);
        commissionBillSaveDto.setCreateTime(now);
        commissionBillSaveDto.setUpdateTime(now);

        if (orderItem.getAgentId().equals(agentId)) {
            // 一级经纪人佣金
            orderSettleUpdateDto.setCommissionAmt(commissionAmt);
            // 设置好下级经纪人id
            if (orderItem.getAgent2Id().compareTo(0L) > 0) {
                commissionBillSaveDto.setSubAgentId(orderItem.getAgent2Id());
            }

            // 统计商家与经纪人的累计结算分销佣金
            UpdateBusinessAgentOrderDto updateBusinessAgentOrderDto = new UpdateBusinessAgentOrderDto();
            updateBusinessAgentOrderDto.setBusinessId(orderItem.getBusinessId());
            updateBusinessAgentOrderDto.setUserId(orderItem.getAgentId());
            updateBusinessAgentOrderDto.setTotalSettleCommission(commissionAmt);
            orderSettleUpdateDto.setUpdateBusinessAgentOrderDto(updateBusinessAgentOrderDto);
        } else {
            // 二级经纪人佣金
            orderSettleUpdateDto.setSubCommissionAmt(commissionAmt);

            // 统计二级经纪人与一级经纪人累计结算分销佣金
            UpdateSubAgentOrderDto updateSubAgentOrderDto = new UpdateSubAgentOrderDto();
            updateSubAgentOrderDto.setAgentId(orderItem.getAgentId());
            updateSubAgentOrderDto.setUserId(orderItem.getAgent2Id());
            updateSubAgentOrderDto.setTotalSettleCommission(commissionAmt);
            orderSettleUpdateDto.setUpdateSubAgentOrderDto(updateSubAgentOrderDto);
        }
        List<CommissionBillSaveDto> list = Optional.ofNullable(orderSettleUpdateDto.getCommissionBillSaves())
                .map(ArrayList::new)
                .orElseGet(ArrayList::new);

        list.add(commissionBillSaveDto);
        orderSettleUpdateDto.setCommissionBillSaves(list);
    }

    private List<MerchantAmtAddDto> getMerchantAmtAdd(List<MerchantAmtBillSaveDto> merchantAmtBillSaveList) {
        if (CollectionUtils.isEmpty(merchantAmtBillSaveList)) {
            return Collections.emptyList();
        }
        Map<Long, List<MerchantAmtBillSaveDto>> groupByMerchantId = merchantAmtBillSaveList.stream()
                .collect(Collectors.groupingBy(MerchantAmtBillSaveDto::getMerchantId));

        List<MerchantAmtAddDto> result = new ArrayList<>();
        for (Map.Entry<Long, List<MerchantAmtBillSaveDto>> entry : groupByMerchantId.entrySet()) {
            MerchantAmtAddDto merchantAmtAddDto = new MerchantAmtAddDto();
            merchantAmtAddDto.setId(entry.getKey());
            merchantAmtAddDto.setBalanceAmt(entry.getValue().stream()
                    .filter(e -> MemberAmtBillTradeTypeEnum.ORDER_AMT.getValue().equals(e.getTradeType()))
                    .map(MerchantAmtBillSaveDto::getTradeAmt).reduce(BigDecimal.ZERO, BigDecimal::add)
            );
            merchantAmtAddDto.setSubsidyAmt(entry.getValue().stream()
                    .filter(e -> MemberAmtBillTradeTypeEnum.ORDER_SUBSIDY.getValue().equals(e.getTradeType()))
                    .map(MerchantAmtBillSaveDto::getTradeAmt).reduce(BigDecimal.ZERO, BigDecimal::add)
            );
            result.add(merchantAmtAddDto);
        }
        return result;
    }

    /**
     * 结算记录转商户资金流水
     * 只有普通商户的本金流水，结算池商户的本金流水在次日有其它任务结算，补贴流水统一在月度结算
     *
     * @param orderSettleBills 结算记录
     * @return
     */
    private List<MerchantAmtBillSaveDto> convertSettleBillsToMerchantAmtBills(SettleParam settleParam, TbOrderItemPo orderItem, List<TbOrderSettleBillPo> orderSettleBills) {
        MerchantDetailVo merchantVo = settleParam.getMerchant(orderItem.getMerchantId());
        if (FinancialAccountTypeEnum.SELF.getValue() != merchantVo.getFinancialAccountType()) {
            return Collections.emptyList();
        }
        return orderSettleBills.stream()
                .filter(settleBill -> OrderSettleBillTradeTypeEnum.MERCHANT_AMT.getValue().equals(settleBill.getTradeType()))
                .map(settleBill -> {
                    MerchantAmtBillSaveDto merchantAmtBillSaveDto = new MerchantAmtBillSaveDto();
                    merchantAmtBillSaveDto.setOrderId(settleBill.getOrderId());
                    merchantAmtBillSaveDto.setBillNo(NoUtils.getMemberAmtBillNo());
                    merchantAmtBillSaveDto.setMerchantId(settleBill.getMerchantId());
                    merchantAmtBillSaveDto.setBillType(BillTypeEnum.INCOME.getValue());
                    merchantAmtBillSaveDto.setTradeType(MemberAmtBillTradeTypeEnum.ORDER_AMT.getValue());
                    merchantAmtBillSaveDto.setTradeAmt(settleBill.getTradeAmt());
                    merchantAmtBillSaveDto.setRelationTradeNo(orderItem.getSubOrderNo());
                    merchantAmtBillSaveDto.setBeforeAmt(merchantVo.getBalanceAmt());
                    BigDecimal afterAmt = merchantVo.getBalanceAmt().add(merchantAmtBillSaveDto.getTradeAmt());
                    merchantVo.setBalanceAmt(afterAmt);
                    merchantAmtBillSaveDto.setAfterAmt(afterAmt);
                    LocalDateTime now = LocalDateTime.now();
                    merchantAmtBillSaveDto.setTradeTime(now);
                    merchantAmtBillSaveDto.setCreateTime(now);
                    merchantAmtBillSaveDto.setUpdateTime(now);
                    return merchantAmtBillSaveDto;
                })
                .collect(Collectors.toList());
    }

    /**
     * 商品本金账单
     */
    private TbOrderSettleBillPo createMerchantAmtSettleBill(SettleParam settleParam, TbOrderItemPo orderItem) {
        if (orderItem.getMerchantAmt().compareTo(BigDecimal.ZERO) <= 0) {
            return null;
        }

        MerchantDetailVo merchantVo = settleParam.getMerchant(orderItem.getMerchantId());

        TbOrderSettleBillPo merchantAmtSettleBill = getCommonOrderSettleBill(settleParam, orderItem);
        merchantAmtSettleBill.setTradeType(OrderSettleBillTradeTypeEnum.MERCHANT_AMT.getValue());
        merchantAmtSettleBill.setTradeAmt(orderItem.getMerchantAmt());
        merchantAmtSettleBill.setReceiveAmt(orderItem.getMerchantAmt());

        // 是否需要统计到月度结算
        if (FinancialAccountTypeEnum.PLATFORM.getValue() == merchantVo.getFinancialAccountType()) {
            merchantAmtSettleBill.setMonthSettleState(MonthSettleStateEnum.WAIT.getValue());
        }

        // 设置汇付账户信息
        setHfAccountInfo(settleParam, merchantAmtSettleBill);

        return merchantAmtSettleBill;
    }

    /**
     * 商品补贴账单
     */
    private TbOrderSettleBillPo createSubsidyAmtSettleBill(SettleParam settleParam, TbOrderItemPo orderItem) {
        if (orderItem.getTotalSubsidyAmt().compareTo(BigDecimal.ZERO) <= 0) {
            return null;
        }

        TbOrderSettleBillPo subsidyAmtSettleBill = getCommonOrderSettleBill(settleParam, orderItem);
        subsidyAmtSettleBill.setTradeType(OrderSettleBillTradeTypeEnum.SUBSIDY_AMT.getValue());
        // 补贴需要加上返现
        BigDecimal subsidyAmt = orderItem.getTotalSubsidyAmt()
                .add(orderItem.getReturnAmt() == null ? BigDecimal.ZERO : orderItem.getReturnAmt());
        subsidyAmtSettleBill.setTradeAmt(subsidyAmt);
        subsidyAmtSettleBill.setReceiveAmt(subsidyAmt);
        // 月度结算
        subsidyAmtSettleBill.setMonthSettleState(MonthSettleStateEnum.WAIT.getValue());

        // 设置汇付账户信息
        setHfAccountInfo(settleParam, subsidyAmtSettleBill);

        return subsidyAmtSettleBill;
    }

    /**
     * 一级经纪人佣金
     */
    private TbOrderSettleBillPo createAgentCommissionSettleBill(TbOrderPo order, TbOrderItemPo orderItem) {
        Long agentId = orderItem.getAgentId();
        Long agent2Id = orderItem.getAgent2Id();
        // 修改为子单核销即得全部佣金
        BigDecimal commissionAmt = Optional.ofNullable(order.getActualCommissionAmt()).orElse(BigDecimal.ZERO);
        // 一级经纪人的佣金需要扣除掉二级经纪人的佣金
        BigDecimal subCommissionAmt = Optional.ofNullable(order.getSubAgentCommissionAmt()).orElse(BigDecimal.ZERO);
        commissionAmt = commissionAmt.subtract(subCommissionAmt);
        if (agentId == null || agentId == 0 || commissionAmt.compareTo(BigDecimal.ZERO) <= 0) {
            return null;
        }

        return getCommonAgentCommissionSettleBill(agentId, agent2Id, commissionAmt, orderItem);
    }

    /**
     * 二级经纪人佣金
     */
    private TbOrderSettleBillPo createSubAgentCommissionSettleBill(TbOrderPo order, TbOrderItemPo orderItem) {
        Long agent2Id = orderItem.getAgent2Id();
        BigDecimal commissionAmt = Optional.ofNullable(order.getSubAgentCommissionAmt()).orElse(BigDecimal.ZERO);
        if (agent2Id == null || agent2Id == 0 || commissionAmt.compareTo(BigDecimal.ZERO) <= 0) {
            return null;
        }
        return getCommonAgentCommissionSettleBill(agent2Id, 0L, commissionAmt, orderItem);
    }

    private TbOrderSettleBillPo getCommonAgentCommissionSettleBill(Long agent1Id, Long agent2Id, BigDecimal commissionAmt, TbOrderItemPo orderItem) {
        // 计算扣除手续费后的佣金      7.5%的手续费，不做四舍五入，去掉第三位
        BigDecimal calTotalCommissionAmt = commissionAmt.divide(Constant.AGENT_AMT_RATE, 2, BigDecimal.ROUND_DOWN);
        BigDecimal feeAmt = commissionAmt.subtract(calTotalCommissionAmt);

        LocalDateTime nowLocalDateTime = LocalDateUtil.getNowLocalDateTime();
        String yyyyMMdd = LocalDateUtil.toLocalDateToString(nowLocalDateTime.toLocalDate(), "yyyyMMdd");
        String reqSeqId = LocalDateUtil.toLocalDateTimeToString(nowLocalDateTime, "yyyyMMddHHmmss") + GenerateUtil.getRandomNum(5);

        TbOrderSettleBillPo tbOrderSettleBillPo = new TbOrderSettleBillPo();
        tbOrderSettleBillPo.setOrderId(orderItem.getOrderId());
        tbOrderSettleBillPo.setOrderItemId(orderItem.getId());
        tbOrderSettleBillPo.setActiveId(orderItem.getActiveId());
        tbOrderSettleBillPo.setBusinessId(orderItem.getBusinessId());
        tbOrderSettleBillPo.setMerchantId(0L);
        tbOrderSettleBillPo.setUserId(orderItem.getUserId());
        tbOrderSettleBillPo.setAgentId(agent1Id);
        tbOrderSettleBillPo.setAgent2Id(agent2Id);
        tbOrderSettleBillPo.setTradeType(OrderSettleBillTradeTypeEnum.COMMISSION_AMT.getValue());
        tbOrderSettleBillPo.setTradeAmt(commissionAmt);
        tbOrderSettleBillPo.setReceiveAmt(calTotalCommissionAmt);
        tbOrderSettleBillPo.setFeeAmt(feeAmt);
        tbOrderSettleBillPo.setOutHfId("");
        tbOrderSettleBillPo.setInHfId("");
        tbOrderSettleBillPo.setInHfAcctId("");
        tbOrderSettleBillPo.setReqDate(yyyyMMdd);
        tbOrderSettleBillPo.setReqSeqId(reqSeqId);
        tbOrderSettleBillPo.setTradeTime(nowLocalDateTime);
        tbOrderSettleBillPo.setCreateTime(nowLocalDateTime);
        tbOrderSettleBillPo.setUpdateTime(nowLocalDateTime);
        tbOrderSettleBillPo.setTradeState(OrderSettleBillTradeStateEnum.WAIT.getValue());

        return tbOrderSettleBillPo;
    }

    private TbOrderSettleBillPo getCommonOrderSettleBill(SettleParam settleParam, TbOrderItemPo orderItem) {
        BusinessDetailVo businessVo = settleParam.getBusiness(orderItem.getBusinessId());
        MerchantDetailVo merchantVo = settleParam.getMerchant(orderItem.getMerchantId());

        TbOrderSettleBillPo orderSettleBill = new TbOrderSettleBillPo();
        orderSettleBill.setOrderId(orderItem.getOrderId());
        orderSettleBill.setOrderItemId(orderItem.getId());
        orderSettleBill.setActiveId(orderItem.getActiveId());
        orderSettleBill.setBusinessId(orderItem.getBusinessId());
        orderSettleBill.setBusinessFinancialAccountType(businessVo.getFinancialAccountType());
        orderSettleBill.setMerchantId(orderItem.getMerchantId());
        orderSettleBill.setMerchantFinancialAccountType(merchantVo.getFinancialAccountType());
        orderSettleBill.setUserId(orderItem.getUserId());
        orderSettleBill.setAgentId(orderItem.getAgentId());
        orderSettleBill.setFeeAmt(BigDecimal.ZERO);
        LocalDateTime now = LocalDateTime.now();
        orderSettleBill.setTradeTime(now);
        orderSettleBill.setCreateTime(now);
        orderSettleBill.setUpdateTime(now);
        orderSettleBill.setTradeState(OrderSettleBillTradeStateEnum.WAIT.getValue());

        return orderSettleBill;
    }

    /**
     * 设置汇付打款账户信息
     */
    private void setHfAccountInfo(SettleParam settleParam, TbOrderSettleBillPo orderSettleBill) {
        MerchantDetailVo merchantVo = settleParam.getMerchant(orderSettleBill.getMerchantId());
        BusinessDetailVo businessVo = settleParam.getBusiness(orderSettleBill.getBusinessId());

        // 如果是补贴，需要设置出款帐号
        if (OrderSettleBillTradeTypeEnum.SUBSIDY_AMT.getValue().equals(orderSettleBill.getTradeType())) {
            String outHfId = businessVo.getHfMerchantNo();
            if (FinancialAccountTypeEnum.PLATFORM.getValue() == businessVo.getFinancialAccountType()) {
                outHfId = hdFinancialAccountProperties.getHfMerchantNo();
            }
            orderSettleBill.setOutHfId(outHfId);
        }

        // 入款账户设置
        String inHfId = "";
        String inHfAcctId = "";
        log.info("SET HF - orderItemId={}", orderSettleBill.getOrderItemId());
        BusinessMerchantVo businessMerchantVo = settleParam.getBusinessMerchant(orderSettleBill.getMerchantId());
        if (businessMerchantVo == null) {
            // 不是商家自有商户
            log.info("SET HF - 1, inHfId={}", inHfId);
            inHfId = merchantVo.getHfMerchantNo();
        } else {
            // 是商家自有商户,则需要获取现金户
            BusinessDetailVo getBusinessDetailVo = settleParam.getBusiness(businessMerchantVo.getBusinessId());
            if (getBusinessDetailVo != null) {
                inHfId = getBusinessDetailVo.getHfMerchantNo();
                inHfAcctId = getBusinessDetailVo.getHfAcctId();
                log.info("SET HF - 2, inHfId={}, inHfAcctId={}", inHfId, inHfAcctId);
            }
        }
        // 如果商户是结算池账户，收款账户为汇付结算池账户
        if (FinancialAccountTypeEnum.PLATFORM.getValue() == merchantVo.getFinancialAccountType()) {
            inHfId = hdFinancialAccountProperties.getHfMerchantNo();
            if (OrderSettleBillTradeTypeEnum.MERCHANT_AMT.getValue().equals(orderSettleBill.getTradeType())) {
                inHfAcctId = hdFinancialAccountProperties.getHfPrincipalAccount();
                log.info("SET HF - 3, inHfAcctId={}", inHfAcctId);
            } else if (OrderSettleBillTradeTypeEnum.SUBSIDY_AMT.getValue().equals(orderSettleBill.getTradeType())) {
                inHfAcctId = hdFinancialAccountProperties.getHfSubsidyInboundAccount();
                log.info("SET HF - 4, inHfAcctId={}", inHfAcctId);
            }
        }
        orderSettleBill.setInHfId(inHfId);
        orderSettleBill.setInHfAcctId(inHfAcctId);
        log.info("orderSettleBill = {}", JSON.toJSONString(orderSettleBill));

        // reqDate & reqSeqId
        LocalDateTime nowLocalDateTime = LocalDateUtil.getNowLocalDateTime();
        orderSettleBill.setReqDate(LocalDateUtil.toLocalDateToString(nowLocalDateTime.toLocalDate(), "yyyyMMdd"));
        orderSettleBill.setReqSeqId(LocalDateUtil.toLocalDateTimeToString(nowLocalDateTime, "yyyyMMddHHmmss") + GenerateUtil.getRandomNum(5));
    }

    private SettleParam initializeSettleParam(List<TbOrderItemPo> orderItems) {
        SettleParam settleParam = new SettleParam();

        List<Long> businessIds = orderItems.stream().map(TbOrderItemPo::getBusinessId).collect(Collectors.toList());
        // 一些商家的商户，也有提供商品给其他商家
        List<Long> merchantIds = orderItems.stream().map(TbOrderItemPo::getMerchantId).collect(Collectors.toList());
        List<MasterBusinessVo> masterBusinessVos = businessMerchantApi.listMasterBusinessByMerchantIds(merchantIds).unpack();
        if (CollectionUtils.isNotEmpty(masterBusinessVos)) {
            List<Long> otherBusinessIds = masterBusinessVos.stream().map(MasterBusinessVo::getBusinessId).collect(Collectors.toList());
            businessIds.addAll(otherBusinessIds);
        }

        // 查询商家数据
        Map<Long, BusinessDetailVo> businessMap = new HashMap<>();
        List<BusinessDetailVo> businessVos = businessApi.listDetailByIds(businessIds).unpack();
        if (!CollectionUtils.isEmpty(businessVos)) {
            businessMap = businessVos.stream().collect(Collectors.toMap(BusinessDetailVo::getId, Function.identity(), (k1, k2) -> k1));
        }
        settleParam.setBusinessMap(businessMap);

        // 查询商家的自有商户
//        List<Long> merchantIds = orderItems.stream().map(TbOrderItemPo::getMerchantId).collect(Collectors.toList());
        Map<Long, BusinessMerchantVo> businessMerchantVoMap = new HashMap<>();
        List<BusinessMerchantVo> ownBusinessMerchants = businessMerchantApi.listOwnMerchantByBusinessIds(businessIds).unpack();
        if (!CollectionUtils.isEmpty(ownBusinessMerchants)) {
            businessMerchantVoMap = ownBusinessMerchants.stream().collect(Collectors.toMap(BusinessMerchantVo::getMerchantId, Function.identity(), (k1, k2) -> k2));
            merchantIds.addAll(ownBusinessMerchants.stream().map(BusinessMerchantVo::getMerchantId).collect(Collectors.toList()));
        }
        settleParam.setBusinessMerchantVoMap(businessMerchantVoMap);

        // 查询商户数据
        Map<Long, MerchantDetailVo> merchantMap = new HashMap<>();
        List<MerchantDetailVo> merchantVos = merchantApi.listDetailByIds(merchantIds).unpack();
        if (!CollectionUtils.isEmpty(merchantVos)) {
            merchantMap = merchantVos.stream().collect(Collectors.toMap(MerchantDetailVo::getId, Function.identity(), (k1, k2) -> k1));
        }
        settleParam.setMerchantMap(merchantMap);

        List<Long> agentIds = orderItems.stream()
                .filter(item -> (item.getAgentId() != null && item.getAgentId() > 0) || (item.getAgent2Id() != null && item.getAgent2Id() > 0))
                .flatMap(item -> Stream.of(item.getAgentId(), item.getAgent2Id())
                        .filter(id -> id != null && id > 0)).distinct().collect(Collectors.toList());
        if (!CollectionUtils.isEmpty(agentIds)) {
            // 查询经纪人入驻的列表，获取对应的落地会员号
            AgentEnterApplyDetailDto agentEnterApplyDetailDto = new AgentEnterApplyDetailDto();
            agentEnterApplyDetailDto.setState(AgentEnterStateEnum.SIGN_SUCCESS.getValue());
            agentEnterApplyDetailDto.setUserIds(agentIds);
            List<AgentEnterApplyDetailVo> agentEnterApplyVos = agentEnterApplyApi.listByDto(agentEnterApplyDetailDto).unpack();
            if (!CollectionUtils.isEmpty(agentEnterApplyVos)) {
                settleParam.setAgentEnterApplyDetailMap(agentEnterApplyVos.stream()
                        .collect(Collectors.groupingBy(AgentEnterApplyDetailVo::getUserId)));
            }

            // 查询经纪人数据
            List<AppUserVo> agentUsers = appUserApi.listByUserIds(agentIds).unpack();
            if (!CollectionUtils.isEmpty(agentUsers)) {
                Map<Long, AppUserVo> agentUserMap = agentUsers.stream()
                        .collect(Collectors.toMap(AppUserVo::getId, Function.identity(), (k1, k2) -> k2));
                settleParam.setAgentUserMap(agentUserMap);
            }
        }

        return settleParam;
    }
}
